/**
 * \file  possible_mutants.h
 * \brief Definition of possible_mutants class.
 *
 *
 * <http://lav.googlecode.com/>
 * Copyright (C) 2013 Alberione Fernando, Ocampo Emanuel, FuDePAN
 *
 * This file is part of the LAV project.
 *
 * Contents:       Header file for biLAV providing class possible_mutants.
 *
 * System:         LAV
 * Language:       C++
 * 
 * Author:         Alberione Fernando
 * E-Mail:         falberione037@gmail.com
 *
 * Author:         Ocampo Emanuel
 * E-Mail:         eocampo86@gmail.com
 *
 * LAV is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LAV is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LAV.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


#ifndef SEQUENCE_MUTANT_H
#define SEQUENCE_MUTANT_H

#include <mili/mili.h>
#include <biopp/biopp.h>
using namespace std;
namespace lav
{

class PossibleMutants
{
public:

    /**
     * This typedef defines the base number
     */
    typedef uint BaseNumber;

    /**
     * This typedef defines multibase number to represent the mutant
     */
    typedef std::vector<BaseNumber> MutantsBases;

    /**
     * This typedef defines the coefficient
     */
    typedef uint64_t Coefficient;

    /**
     * This typedef defines the coefficients to calculate the id of each mutant
     */
    typedef std::vector<Coefficient> MutantsCoefficients;

    /**
     * This typedef defines multibase number digit
     */
    typedef uint MultibaseNumberDigit;

    /**
     * This typedef defines a mutant sequence
     */
    typedef std::vector<MultibaseNumberDigit> MutantSeq;

    /**
     * This typedef defines the id of a mutant
     */
    typedef uint64_t Id;

    /**
     * This typedef defines a populations Ids
     */
    typedef std::list <Id> PopulationsIds;

    /**
     * This typedef defines a container mutant sequence
     */
    typedef std::list<MutantSeq> MutantSeqContainer;

    /**
     * This typedef represents a vector of triplets (aminoacid)
     */
     typedef std::vector<biopp::Triplet> TripletVector;

    /**
     * This struct defines the aminoacid and the triplets that code
     */
    struct AminoacidTriplets
    {
        std::string aminoacid;
        TripletVector triplets;

       /**
        * Constructor that add new aminoacid
        * This constructor will should be used when add the new aminoacid
        * to position resistence in join
        * @param amin aminoacid added 
        */
        AminoacidTriplets(const char& amin)
        {
            aminoacid += amin;
        }

       /**
        * This method returns the triplet represented for the digit of multibase number
        * @param digit multibase number digit
        * @param triplet triplet represented
        */
        void get_triplet(MultibaseNumberDigit digit, biopp::Triplet& triplet) const;
    };

    /**
    * This typedef represents a list of AminoacidTriplets
    */
    typedef std::list<AminoacidTriplets> AminoacidTripletsContainer;

    /**
    * This structure defines a position of resistance with its aminoacids and triplets
    */
    struct PosAminoacidsTriplets
    {
        size_t pos_resistance;
        AminoacidTripletsContainer aminoacids_triplets;

       /**
        * Constructor that add new position of resitance
        * This constructor will should be used when add the new position resistance in join
        * @param pos position of resistance 
        */
        PosAminoacidsTriplets(size_t pos): pos_resistance(pos)
        {}

        /**
        *  This method returns the triplet represented for the multibase number digit
        * @param value position in the vector triplet
        * @param triplet codon in value
        */
        void get_triplet_of_digit(MultibaseNumberDigit digit, biopp::Triplet& triplet) const;

       /**
        * This method returns the digit (for multibase number) represents the triplet
        * @param triplet codon
        * @param digit digit represents the triplet (in multibase number)
        */
        void get_digit_of_triplet(const biopp::Triplet& triplet, MultibaseNumberDigit& digit) const;

    private:

       /**
        * This method search the position of triplet
        * @param it_amin_triplets current "aminoacids_triplets"
        * @param pos position of triplet
        */
        void search_position_triplet(AminoacidTripletsContainer::const_iterator& it_amin_triplets,
                                     uint& pos) const;


       /**
        * This method returns true if exist triplet in list triplets
        * @param triplet codon
        * @param it_amin_triplets current list of triplets
        * @param sum_triplets number of codons evaluated until "triplet" is found
        */
        bool exist_triplet(const biopp::Triplet& triplet, const AminoacidTripletsContainer::const_iterator& it_amin_triplets,
                           uint& sum_triplets) const;
    };

    /**
    * This typedef represents a vector of the struct Pos_Aminoacids_Triplets
    */
    typedef std::vector<PosAminoacidsTriplets> PosAminoacidsTripletsContainer;

    /**
     * This struct defines a position of resistance
     */
    struct TripletPosition
    {
        size_t pos;
        biopp::Triplet triplet;

       /**
        * Constructor that add new position of resitance
        * This constructor will should be used when add the new position resistance
        * @param pos position of resistance 
        * @param triplet codon 
        */
        TripletPosition(const size_t pos, const biopp::Triplet triplet)
            : pos(pos), triplet(triplet)
        {}
    };

    /**
     * This typedef represents a list of triplets (aminoacid)
     */
    typedef std::list<TripletPosition> TripletPositionContainer;

    /**
     * class constructor
     * @param join_antivirals join of resistance by Position
     */
    PossibleMutants(const PosAminoacidsTripletsContainer& join_antivirals)
        : mutantSize(join_antivirals.size())
    {
        mutantsBases.resize(mutantSize);
        assert(mutantSize == mutantsBases.size());
        get_mutant_bases(join_antivirals);
        mutantsCoefficients.resize(mutantSize);
        mutantsCoefficients[0] = 1;
        for (size_t i = 1; i < mutantSize; ++i)
        {
            mutantsCoefficients[i] = mutantsCoefficients[i - 1] * mutantsBases[mutantSize - i];
        }
        maxNumberMutants = (mutantsBases[0] * mutantsCoefficients[mutantSize - 1]) - 1;
    }


    /**
     * This method returns the maximum number of possible mutants
     */
    Id get_maxNumber_mutants() const;


    /**
     * This method returns the size of the mutant
     */
    size_t get_mutantSize() const;


    /**
     * This method returns the number of bases that define the mutant
     */
    size_t get_numberBases() const;


    /**
     * This method returns the number of coefficient to calculate the id of each mutant
     */
    size_t get_numberMutants_coefficients() const;


    /**
     * This method returns based on pos
     */
    size_t get_base(size_t pos) const;


   /**
     * This method returns coefficient in pos
     */
    size_t get_coefficient(size_t pos) const;


   /**
    * This method returns the id representing the mutant
    * @param mutant represents patient's virus
    */
    Id get_id(const MutantSeq& mutant) const;


   /**
    * This method computes the mutant represented by id
    * @param id representing the mutant
    * @param mutant represents patient's virus
    */
    void get_mutant(Id id, MutantSeq& mutant) const;


   /**
    * This method converts a mutant (number base mixed) to a list of struct (pos, triplet)
    * @param join_antivirals joining the resistance according to their position
    * @param mutant represents patient's virus (number base mixed)
    * @param triplets_positions list of struct (pos, triplet)
    */
    void mutant_to_triplets(const PossibleMutants::PosAminoacidsTripletsContainer& join_antivirals,
                            const PossibleMutants::MutantSeq& mutant, PossibleMutants::TripletPositionContainer& triplets_positions) const;


private:
    const uint mutantSize;
    MutantsBases mutantsBases;
    MutantsCoefficients mutantsCoefficients;
    Id maxNumberMutants;


   /**
    * This method returns the base for the mutants (represented as multibase number)
    * @param join_antivirals joining the resistance according to their position
    */
    void get_mutant_bases(const PosAminoacidsTripletsContainer& join_antivirals);

   /**
    * This method returns the summation of triplets in current AminoacidTripletsContainer
    * @param amins_triplets current AminoacidTripletsContainer 
    */
    uint get_sum_triplets(const AminoacidTripletsContainer& amins_triplets);
};
}
#endif
